package cn.sdfykjyxgs.synclient.socket;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import cn.sdfykjyxgs.synclient.base.HttpResult;
import cn.sdfykjyxgs.synclient.entity.FileSynConfig;
import cn.sdfykjyxgs.synclient.server.FileSynConfigService;
import cn.sdfykjyxgs.synclient.util.MyFileLogQuery;
import cn.sdfykjyxgs.syncommon.entity.FileChangeLog;
import cn.sdfykjyxgs.syncommon.util.MessageBufferSize;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.*;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Configuration
public class MyWebSocketConfig {
    @Resource
    private FileSynConfigService synConfigService;
    private static String WS_ENDPOINT = "ws://localhost:8080/websocket"; // WebSocket服务端地址


    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        // 在此处设置bufferSize
        container.setMaxTextMessageBufferSize(MessageBufferSize.maxSize);
        container.setMaxBinaryMessageBufferSize(MessageBufferSize.maxSize);
        container.setMaxSessionIdleTimeout(15 * 60000L);
        return container;
    }


    @Bean
    public WebSocketClient webSocketClient() {
        WebSocketClient webSocketClient = new StandardWebSocketClient();
        ApplicationContext context = SpringUtil.getApplicationContext();
        FileSynConfigService bean = context.getBean(FileSynConfigService.class);
        HttpResult httpResult = bean.configPageData();
        FileSynConfig config = (FileSynConfig) httpResult.getData();
        if (config != null) {
            WS_ENDPOINT = "ws://" + config.getIp() + ":" + config.getPort() + "/websocket";
            WebSocketConnectionHandler connectionHandler = new WebSocketConnectionHandler(webSocketClient, WS_ENDPOINT);
            // 添加定时任务，定时尝试重连
            ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
            executor.scheduleWithFixedDelay(() -> {
                if (!connectionHandler.isConnected()) {
                    connectionHandler.connect(); // 重连
                }
            }, 0, 10, TimeUnit.SECONDS);
        }
        return webSocketClient;
    }

    private  class WebSocketConnectionHandler implements WebSocketHandler {
        private final WebSocketClient webSocketClient;
        private final String wsEndpoint;
        private volatile boolean connected;

        public WebSocketConnectionHandler(WebSocketClient webSocketClient, String wsEndpoint) {
            this.webSocketClient = webSocketClient;
            this.wsEndpoint = wsEndpoint;
        }

        public void connect() {
            try {
                webSocketClient.doHandshake(this, String.valueOf(new URI(wsEndpoint)));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public boolean isConnected() {
            return connected;
        }

        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            connected = true;
            session.setTextMessageSizeLimit(512000);
            session.setBinaryMessageSizeLimit(512000);
            HttpResult httpResult = synConfigService.configPageData();
            FileSynConfig data = (FileSynConfig) httpResult.getData();
            cn.sdfykjyxgs.syncommon.socket.WebSocketMessage webSocketMessage = new cn.sdfykjyxgs.syncommon.socket.WebSocketMessage("","90001",JSONUtil.parseObj(data));
            session.sendMessage(new TextMessage(JSONUtil.toJsonStr(webSocketMessage)));
            System.out.println("Connected to WebSocket server");
        }

        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
            connected = false;
            System.out.println("Connection to WebSocket server closed. Reason: " + closeStatus.getReason());
        }

        /**
         * Whether the WebSocketHandler handles partial messages. If this flag is set to
         * {@code true} and the underlying WebSocket server supports partial messages,
         * then a large WebSocket message, or one of an unknown size may be split and
         * maybe received over multiple calls to
         * {@link #handleMessage(WebSocketSession, WebSocketMessage)}. The flag
         * {@link WebSocketMessage#isLast()} indicates if
         * the message is partial and whether it is the last part.
         */
        @Override
        public boolean supportsPartialMessages() {
            return false;
        }

        /**
         * Invoked when a new WebSocket message arrives.
         *
         * @param session
         * @param message
         * @throws Exception this method can handle or propagate exceptions; see class-level
         *                   Javadoc for details.
         */
        @Override
        public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
            String messageStr = message.getPayload().toString();
            cn.sdfykjyxgs.syncommon.socket.WebSocketMessage webSocketMessage = JSONUtil.toBean(messageStr, cn.sdfykjyxgs.syncommon.socket.WebSocketMessage.class);
            List<FileChangeLog> logs = webSocketMessage.getLogs();
            logs.forEach(item -> {
                MyFileLogQuery.put(item);
            });
        }

        /**
         * Handle an error from the underlying WebSocket message transport.
         *
         * @param session
         * @param exception
         * @throws Exception this method can handle or propagate exceptions; see class-level
         *                   Javadoc for details.
         */
        @Override
        public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

        }
    }
}
